package system

import (
	"context"
	"gf-vben-admin/consts"
	"gf-vben-admin/internal/model/auth"
	model "gf-vben-admin/internal/model/system"
	"gf-vben-admin/internal/model/system/entity"
	"gf-vben-admin/internal/service/system/internal/dao"
	utils "gf-vben-admin/utility"
	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"time"
)

type (
	sUser struct{}
)

var (
	insUser = sUser{}
)

func User() *sUser {
	return &insUser
}

func (s *sUser) PageList(ctx context.Context, param model.User) (total int, result []*entity.User, err error) {
	result = make([]*entity.User, 0)
	daoModel := dao.User.Ctx(ctx)
	columnMap := dao.User.ColumnMap()
	daoModel, err = utils.GetWrapper(param, daoModel, columnMap)
	if err != nil {
		return
	}
	total, err = daoModel.Count()
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("获取总行数失败")
		return
	}
	if total == 0 {
		return
	}
	err = daoModel.Page(param.PageNum, param.PageSize).Scan(&result)
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("获取数据失败")
	}
	return
}

func (s *sUser) List(ctx context.Context, param model.User) (result []*entity.User, err error) {
	result = make([]*entity.User, 0)
	daoModel := dao.User.Ctx(ctx)
	columnMap := dao.User.ColumnMap()
	daoModel, err = utils.GetWrapper(param, daoModel, columnMap)
	if err != nil {
		return
	}
	daoModel, err = utils.GetWrapper(param, daoModel, columnMap)
	if err != nil {
		return
	}
	err = daoModel.Scan(&result)
	if err != nil {
		g.Log().Error(ctx, err)
		err = gerror.New("获取数据失败")
	}
	return
}

func (s *sUser) Add(ctx context.Context, in model.User) (err error) {
	err = dao.User.Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		_, err := tx.Model(dao.User.Table()).Ctx(ctx).OmitEmpty().Data(in).Insert()
		return err
	})
	return
}

func (s *sUser) Edit(ctx context.Context, in model.User) (err error) {
	err = dao.User.Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		_, err := tx.Model(dao.User.Table()).Ctx(ctx).OmitEmpty().Data(in).Where(g.Map{
			dao.User.Columns().Id:      in.Id,
			consts.VersionNumberColumn: in.VersionNumber,
		}).Update()
		return err
	})
	return
}

func (s *sUser) EditState(ctx context.Context, ids []int64, state int8) (err error) {
	err = dao.User.Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		_, err := tx.Model(dao.User.Table()).Ctx(ctx).Data(g.Map{consts.StateColumn: state}).Where(g.Map{
			dao.User.Columns().Id: ids,
		}).Update()
		return err
	})
	return
}

func (s *sUser) Delete(ctx context.Context, ids []int64) (err error) {
	err = dao.User.Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		_, err := tx.Model(dao.User.Table()).Ctx(ctx).Where(g.Map{
			dao.User.Columns().Id: ids,
		}).Delete()
		return err
	})
	return
}

func (s *sUser) GetAdminByUsernameAndPwd(ctx context.Context, req auth.LoginParamsReq) (user *entity.User, err error) {
	user, err = s.GetUserByUsernamePassword(ctx, req)

	return user, nil
}

// GetUserByUsernamePassword 登陆验证
func (s *sUser) GetUserByUsernamePassword(ctx context.Context, req auth.LoginParamsReq) (user *entity.User, err error) {
	user, err = s.GetUserByUsername(ctx, req.LoginName)
	if err != nil {
		return
	}
	if user == nil {
		return nil, gerror.New("账号不存在")
	}
	//验证密码
	if gmd5.MustEncryptString(gmd5.MustEncryptString(req.Password)) != user.HashPassword {
		t := s.setLoginCommit(ctx, req.Ip)
		return nil, gerror.Newf("密码错误，错误%d次后，账户将被锁定30分钟", t)
	}
	//账号状态
	if user.State == 0 {
		return nil, gerror.New("账号已被冻结")
	}
	user.LastLoginAt = gtime.Now()
	_, err = dao.User.Ctx(ctx).Data(user).Where(dao.User.Columns().Id, user.Id).Update()
	user.HashPassword = ""
	return
}

// GetUserByUsername 通过用户名获取用户信息
func (s *sUser) GetUserByUsername(ctx context.Context, loginName string) (user *entity.User, err error) {
	err = dao.User.Ctx(ctx).
		Where(dao.User.Columns().LoginName, loginName).
		WhereOr(dao.User.Columns().Email, loginName).
		WhereOr(dao.User.Columns().Phone, loginName).
		Scan(&user)
	if err != nil {
		return nil, err
	}
	return
}

func (s *sUser) setLoginCommit(ctx context.Context, ip string) int {
	cacheKey := consts.LoginLimit + consts.SymbolColon
	count, _ := g.Redis().Do(ctx, "GET", cacheKey+ip)

	surplusCount := 5
	if count.IsEmpty() {
		countTemp := count.Int()
		countTemp++
		surplusCount -= countTemp
		_, _ = g.Redis().Do(ctx, "SETEX", cacheKey+ip, 10*time.Second, countTemp)
	} else {
		surplusCount--
		_, _ = g.Redis().Do(ctx, "SETEX", cacheKey+ip, 30*time.Second, 1)
	}
	return surplusCount
}
